package client;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.eclipse.swt.SWT;
import org.eclipse.swt.browser.Browser;
import org.eclipse.swt.browser.CloseWindowListener;
import org.eclipse.swt.browser.LocationEvent;
import org.eclipse.swt.browser.LocationListener;
import org.eclipse.swt.browser.ProgressEvent;
import org.eclipse.swt.browser.ProgressListener;
import org.eclipse.swt.browser.StatusTextEvent;
import org.eclipse.swt.browser.StatusTextListener;
import org.eclipse.swt.browser.WindowEvent;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.ShellEvent;
import org.eclipse.swt.events.ShellListener;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.layout.FormLayout;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class AdvancedBrowser {
	private static final Logger logger = Logger.getLogger(AdvancedBrowser.class.getName());
	private static final String AT_REST = "Ready";
	private static final String serverURL = StaticDefinitions.serverURL;
	private static final int port = StaticDefinitions.port;
	private static final String feedbackHost = StaticDefinitions.feedbackHost;
	
	private static final String bookmarkPath = "bookmarks";
	private HashSet<String> bookmarks = new HashSet<String>();
	
	private String previousLocation = "";
	private String currentLocation = "";
	private long enteredSE;
	private long wentToSearchResult;
	private String urlToSet = null;
	
	private Browser browser;

	public AdvancedBrowser(String location) {
		
		currentLocation = location;
		loadBookmarks();
		
		Display display = new Display();
		Shell shell = new Shell(display);
		shell.setText("Advanced Browser");

		shell.setLayout(new FormLayout());

		Composite controls = new Composite(shell, SWT.NONE);
		FormData data = new FormData();
		data.top = new FormAttachment(0, 0);
		data.left = new FormAttachment(0, 0);
		data.right = new FormAttachment(100, 0);
		controls.setLayoutData(data);

		Label status = new Label(shell, SWT.NONE);
		data = new FormData();
		data.left = new FormAttachment(0, 0);
		data.right = new FormAttachment(100, 0);
		data.bottom = new FormAttachment(100, 0);
		status.setLayoutData(data);

		browser = new Browser(shell, SWT.BORDER);
		data = new FormData();
		data.top = new FormAttachment(controls);
		data.bottom = new FormAttachment(status);
		data.left = new FormAttachment(0, 0);
		data.right = new FormAttachment(100, 0);
		browser.setLayoutData(data);

		controls.setLayout(new GridLayout(8, false));

		Button button = new Button(controls, SWT.PUSH);
		button.setText("Back");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				browser.back();
				feedbackSendBack();
			}
		});

		button = new Button(controls, SWT.PUSH);
		button.setText("Forward");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				browser.forward();
			}
		});

		button = new Button(controls, SWT.PUSH);
		button.setText("Refresh");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				browser.refresh();
			}
		});

		button = new Button(controls, SWT.PUSH);
		button.setText("Stop");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				browser.stop();
			}
		});

		button = new Button(controls, SWT.PUSH);
		button.setText("Bookmark");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				callBookmarkSelector();
			}
		});

		final Text url = new Text(controls, SWT.BORDER);
		url.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		url.setFocus();

		button = new Button(controls, SWT.PUSH);
		button.setText("Go");
		button.addSelectionListener(new SelectionAdapter() {
			public void widgetSelected(SelectionEvent event) {
				browser.setUrl(url.getText());
			}
		});

		Label throbber = new Label(controls, SWT.NONE);
		throbber.setText(AT_REST);

		shell.setDefaultButton(button);

		browser.addCloseWindowListener(new AdvancedCloseWindowListener());
		browser.addLocationListener(new AdvancedLocationListener(url));
		browser.addProgressListener(new AdvancedProgressListener(throbber));
		browser.addStatusTextListener(new AdvancedStatusTextListener(status));

		shell.addShellListener(new ShellListener() {
			@Override
			public void shellIconified(ShellEvent e) {}
			@Override
			public void shellDeiconified(ShellEvent e) {}
			@Override
			public void shellDeactivated(ShellEvent e) {}
			@Override
			public void shellClosed(ShellEvent e) {
				System.exit(0);
			}
			@Override
			public void shellActivated(ShellEvent e) {}
		});
		
		// Go to the initial URL
		if (location != null) {
			browser.setUrl(location);
		}

		shell.open();
		while (!shell.isDisposed()) {
			if (urlToSet != null) {
				browser.setUrl(urlToSet);
				urlToSet = null;
			}
			if (!display.readAndDispatch()) {
				display.sleep();
			}
		}
		display.dispose();
	}
	
	protected void setUrl(String url) {
		urlToSet = url;
	}
	
	protected void bookmark() {
		addToBookmarks();
		sendBookmark();
	}
	
	private void callBookmarkSelector() {
		new BookmarksSelector(bookmarks, this);
	}

	protected void addToBookmarks() {
		bookmarks.add(currentLocation);
		saveBookmarks();
	}
	
	protected void loadBookmarks() {
		FileInputStream fstream = null;
		DataInputStream in = null;
		BufferedReader br = null;
		
		try {
			fstream = new FileInputStream(bookmarkPath);
			in = new DataInputStream(fstream);
			br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			
			while ((strLine = br.readLine()) != null) {
				bookmarks.add(strLine);
			}
			
		} catch (FileNotFoundException e) {
			logger.log(Level.SEVERE, "Load bkmrks FNF", e);
		} catch (IOException e) {
			logger.log(Level.SEVERE, "Load bkmrks IOE", e);
		} finally {
			try {
				br.close();
				in.close();
				fstream.close();
			} catch (IOException e) {}
		}
	}
	
	protected void saveBookmarks() {
		File bkmarkFile;
		PrintStream bkOut = null;
		try {
			bkmarkFile = new File(bookmarkPath);
			bkOut = new PrintStream(bkmarkFile);
			
			for (String bk : bookmarks) {
				bkOut.println(bk);
			}
			bkOut.flush();
			
		} catch (FileNotFoundException e) {
			logger.log(Level.SEVERE, "Save bkmrks", e);
		} finally {
			if (bkOut != null)
				bkOut.close();
		}
	}
	
	protected void sendBookmark() {
		String params = "page=" + currentLocation;
		sendFeedback("bookmark", params);
	}

	protected void feedbackSendBack() {
		sendFeedback("back=true", "page=" + currentLocation);
	}
	
	@SuppressWarnings("unused")
	private void sendFeedback(HashMap<String, String> params) {
		StringBuffer stb = new StringBuffer();
		
		for (String key : params.keySet()) {
			stb.append(key).append('=').append(params.get(key)).append('&');
		}
		sendFeedback(stb.substring(0, stb.length()), "");
	}

	private void sendFeedback(String urlParameters, String content) {
		HttpURLConnection connection = null;
		
		System.out.println(urlParameters + " & " + content);
		
		try {
			URL url = new URL(feedbackHost + '?' + urlParameters);
			connection = (HttpURLConnection) url.openConnection();
			connection.setRequestMethod("POST");
			connection.setRequestProperty("Content-Type",
					"application/x-www-form-urlencoded");

			connection.setRequestProperty("Content-Length",
					"" + Integer.toString(content.getBytes().length));
			connection.setRequestProperty("Content-Language", "en-US");

			connection.setUseCaches(false);
			connection.setDoInput(true);
			connection.setDoOutput(true);
			
			
			connection.setReadTimeout(10000);
			connection.setConnectTimeout(15000);
			
			DataOutputStream wr = new DataOutputStream(
					connection.getOutputStream());
			wr.writeBytes(content);
			wr.flush();
			wr.close();

			int code = connection.getResponseCode();
			
			System.out.println(code);
			
			connection.disconnect();
			
		} catch (MalformedURLException e) {
			logger.log(Level.SEVERE, "Invalid url", e);
		} catch (IOException e) {
			logger.log(Level.SEVERE, "IO error", e);
		} finally {
			if (connection != null) {
				connection.disconnect();
			}
		}
	}

	class AdvancedCloseWindowListener implements CloseWindowListener {
		public void close(WindowEvent event) {
			((Browser) event.widget).getShell().close();
		}
	}

	class AdvancedLocationListener implements LocationListener {
		private Text location;

		public AdvancedLocationListener(Text text) {
			location = text;
		}

		public void changing(LocationEvent event) {
			location.setText("Loading " + event.location + "...");
		}

		public void changed(LocationEvent event) {
			
			if (currentLocation.startsWith(serverURL)) {
				// we are already on the SE
				if (event.location.startsWith(serverURL)) {
					// we are moving through the SE
				} else {
					
					// we left the SE
					wentToSearchResult = System.nanoTime();
					long timespent = wentToSearchResult - enteredSE;
					String params = "timeOnSE=" + Long.toString(timespent);
					sendFeedback(params, "");
				}
			} else {
				if (event.location.startsWith(serverURL)) {
					// we arrived on the SE
					enteredSE = System.nanoTime();
				} else {
					// we are browsing the web
					
				}
			}
			
			previousLocation = location.getText();
			location.setText(event.location);
			currentLocation = event.location;
		}
	}

	class AdvancedProgressListener implements ProgressListener {
		private Label progress;

		public AdvancedProgressListener(Label label) {
			progress = label;
		}

		public void changed(ProgressEvent event) {
			if (event.total != 0) {
				int percent = (int) (event.current / event.total);
				progress.setText(percent + "%");
			} else {
				progress.setText("?");
			}
		}

		public void completed(ProgressEvent event) {
			progress.setText(AT_REST);
		}
	}

	class AdvancedStatusTextListener implements StatusTextListener {
		private Label status;

		public AdvancedStatusTextListener(Label label) {
			status = label;
		}

		public void changed(StatusTextEvent event) {
			status.setText(event.text);
		}
	}

	public static void main(String[] args) {
		new AdvancedBrowser(StaticDefinitions.serverURL);
	}
}